#pragma once
#include <iostream>
#include <vector>
#include <cassert>
#include <semaphore.h>
#include <pthread.h>
using namespace std;
static const int g_maxnum = 5;

template<class T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        // 减少信号量
        int n = sem_wait(&sem);
        assert(n == 0);
        (void)n;
    }
    void V(sem_t &sem)
    {
        //增加信号量
        int n = sem_post(&sem);
        assert(n == 0);
        (void)n;
    }
public:
    RingQueue(const int &setnum = g_maxnum): _queue(setnum), _maxnum(setnum)
    {
        int a = sem_init(&_spaceSem, 0, _maxnum);
        assert(a == 0);
        int b = sem_init(&_dataSem, 0, 0);
        assert(b == 0);

        _productorStep = _consumerStep = 0; // 初始化下标都从0开始

        pthread_mutex_init(&_pmutex, nullptr);
        pthread_mutex_init(&_cmutex, nullptr);
    }
    // 生产者
    void Push(const T &in)
    {
        P(_spaceSem); //减少/等待空间信号量 如果队列满则阻塞 直到有空间可写

        pthread_mutex_lock(&_pmutex); //加锁   
        _queue[_productorStep++] = in;
        _productorStep %= _maxnum; //保证在范围内循环 
        pthread_mutex_unlock(&_pmutex); //解锁

        V(_dataSem); //增加数据信号量 通知消费者有新数据可用
    }
    // 消费者
    void Pop(T *out)
    {
        P(_dataSem); //减少/等待数据信号量 如果队列为空则阻塞 直到有数据可读

        pthread_mutex_lock(&_cmutex); //加锁
        *out = _queue[_consumerStep++];
        _consumerStep %= _maxnum; //保证在范围内循环 
        pthread_mutex_unlock(&_cmutex); //解锁

        V(_spaceSem); //增加空间信号量 通知生产者有空闲位置可写
    }
    ~RingQueue()
    {
        sem_destroy(&_spaceSem);
        sem_destroy(&_dataSem);

        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }
private:
    vector<T> _queue;
    int _maxnum;
    sem_t _spaceSem; // 生产者 想生产 看剩余的空间资源
    sem_t _dataSem;  // 消费者 想消费 看剩余的数据资源
    int _productorStep; //标识生产者下标
    int _consumerStep; //标识消费者下标
    pthread_mutex_t _pmutex;
    pthread_mutex_t _cmutex;

};